#pragma once

#include <vex/core/make_raw_handle.h>
#include <vex/core/as_intrusive.h>

namespace vex {
    namespace core {
        using implementation::handle_from_base;

#ifdef VEX_HAS_CPP_11_FEATURE_SET_3
        template < class TBase, class ...TArgs > auto make_intrusive_handle(TArgs&& ...p_args)
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return vex::as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArgs>(p_args)...));
        }
#else
        template < class TBase > auto make_intrusive_handle()
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return vex::as_intrusive_from_out(make_raw_handle<TBase>());
        }

        template <class TBase, class TArg1> auto make_intrusive_handle(
            TArg1&& p_arg1) 
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArg1>(p_arg1)));
        }

        template <class TBase, class TArg1, class TArg2> auto make_intrusive_handle(
            TArg1 && p_arg1, TArg2 && p_arg2) 
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2)));
        }

        template <class TBase, class TArg1, class TArg2, class TArg3> auto make_intrusive_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3) 
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3))); 
        }

        template <class TBase, class TArg1, class TArg2, class TArg3, class TArg4> auto make_intrusive_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4) 
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3), 
                std::forward<TArg4>(p_arg4))); 
        }

        template <class TBase, class TArg1, class TArg2, class TArg3, class TArg4, class TArg5> auto make_intrusive_handle(
            TArg1 && p_arg1, TArg2 && p_arg2, TArg3 && p_arg3, TArg4 && p_arg4, TArg5&& p_arg5) 
            -> decltype(vex::as_intrusive_from_out(std::declval<TBase*>()))
        {
            return as_intrusive_from_out(make_raw_handle<TBase>(
                std::forward<TArg1>(p_arg1), 
                std::forward<TArg2>(p_arg2), 
                std::forward<TArg3>(p_arg3), 
                std::forward<TArg4>(p_arg4),
                std::forward<TArg4>(p_arg5))); 
        }
#endif
    }

    using core::make_intrusive_handle;
}